遍历和谓词

迭代器使我们能写出迭代穿过一个序列的循环。当然,总写循环也会使人厌倦,为此,标准库提供了一些能对序列中的每个元素调用一个函数的方法。

考虑去写一个程序,它从输入中读一些单词,并记录下它们出现的频率。表示字符串及其相关频率的最明显方式是采用一个 map

    map<string, int> histogram;

对每个串记录其频率,需要做的明显操作是

    void record(const string& s)
    {
        histogram[s]++;    // 记录“s”出现的次数
    }

一旦读完了输入,我们还可能想要输出工作中收集到的数据。这个 map 由一个 (string,int) 对的序列组成,因此,我们就希望对 map 中的每个元素调用下面函数:

    void print(const pair<const string, int>& r)
    {
        cout << r.first << ' ' << r.second << '\n';
    }

pair 的第一个元素被称为 first,第二个元素被称为 second)。pair 的第一个元素是 const string,而不是普通的 string,因为 map 里所有的关键码都是常量。

这样,主程序就可以写成:

    int main()
    {
        istream_iterator<string> ii(cin);
        istream_iterator<string> eos;

        for_each(ii, eos, record);            // #include <algorithm>
        for_each(histogram.begin(), histogram.end(), print);
    }

请注意,我们不需要对 map 排序以便使输出是按顺序的,因为 map 总是按顺序保存它的元素,因此,它的迭代器也将按(上升)序遍历 map 的所有元素。

许多程序设计工作都涉及到在容器中寻找某些东西,而不是简单地对每个元素做某件事情。例如,find 算法(18.5.2节)是为寻找某个特定值提供的一种很方便的方式。这一思想的另一种更一般的变形是寻找满足某种特殊需要的元素。例如,我们可能需要在一个 map 里寻找第一个大于 42 的值。一个 map 就是一个(关键码,值)对偶的序列,因此我们需要检索这个序列,去找一个 pair< const string, int>,其中的 int 大于 42

    bool gt_42(const pair<const string, int>& r)
    {
        return r.senond > 42;
    }

    void f(map<string, int>& m)
    {
        typedef map<string,int>::const_iterator M1;
        M1 i = find_if(m.begin(), m.end(), gt_42);
        // ...
    }

换一种情况,我们也可能需要统计频率高于 42 的单词的个数:

    void g(const map<string, int>& m)
    {
        int c42 = count_if(m.begin(), m.end(), gt_42);
        // ...
    }

gt_42() 这样用于控制算法的函数被称为谓词。谓词将针对每个元素调用并返回布尔值,算法根据这种返回值去执行自己预定的动作。例如,find_if() 将检索到它的谓词返回 true 为止,这说明找到了一个要找的元素。与此类似,count_if() 统计出它的谓词返回 true 的次数。

标准库提供了若干很有用的谓词,以及一些能用于构造出更多谓词的模板(18.4.2节)。

🔚